/*
# File    :   kbhit.c
# Descrb  :   A function meet "kbhit" for Linux.
# Author  :   SCHIPS
# Mail    :   schips@dingtalk.com
# Date    :   Thu, May 16, 2019  2:44:47 PM
*/

#include <stdio.h>
#include <stdlib.h>
#include <termios.h>
#include <unistd.h>

// 1 首先我们需要定义标准的头文件并且为终端设置声明了一个结构。peek_character用于测试一个按键是否被按下。然后我们定义了我们将会用到的函数的原型。
static struct termios initial_settings, new_settings;
static int peek_character = -1;
void init_keyboard();
void close_keyboard();
int kbhit();
int readch();

//2 main函数调用init_keyboard函数来配置终端，然后一秒循环一次，调用kbhit函数。如果按键检测为q，close_keyboard函数会返回正常行为并且退出程序。
int main()
{
    int ch = 0;
    printf("Hit [q] to exit.\n");
    init_keyboard();
    while(ch != 'q') {
        printf("looping\n");
        sleep(1);
        if(kbhit()) {
            ch = readch();
            printf("you hit %c\n",ch);
        }
    }
    close_keyboard();
    exit(0);
}

//3 init_keyboard与close_keyboard在程序的开始和结束配置终端。
void init_keyboard()
{
    tcgetattr(0,&initial_settings);
    new_settings = initial_settings;
    new_settings.c_lflag &= ~ICANON;
    new_settings.c_lflag &= ~ECHO;
    new_settings.c_lflag &= ~ISIG;
    new_settings.c_cc[VMIN] = 1;
    new_settings.c_cc[VTIME] = 0;
    tcsetattr(0, TCSANOW, &new_settings);
}

void close_keyboard()
{
    tcsetattr(0, TCSANOW, &initial_settings);
}

//4 下面是检测键盘按键的函数：
int kbhit()
{
    char ch;
    int nread;

    if(peek_character != -1)
        return 1;

    new_settings.c_cc[VMIN]=0;
    tcsetattr(0, TCSANOW, &new_settings);
    nread = read(0,&ch,1);
    new_settings.c_cc[VMIN]=1;
    tcsetattr(0, TCSANOW, &new_settings);

    if(nread == 1) {
        peek_character = ch;
        return 1;
    }

    return 0;
}

// 5 按下的按键是由下一个函数，readch，读取的，然后将peek_character为下一次循环设置为-1。
int readch()
{
    char ch;
    if(peek_character != -1) {
        ch = peek_character;
        peek_character = -1;
        return ch;
    }

    read(0,&ch,1);

    return ch;
}
